package cn.itcast.account.service.impl;

import cn.itcast.account.entity.AccountFreeze;
import cn.itcast.account.mapper.AccountFreezeMapper;
import cn.itcast.account.mapper.AccountMapper;
import cn.itcast.account.service.AccountTCCService;
import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * Author: Zheng
 * Date:   2024/3/26  16:12
 * Package：cn.itcast.account.service.impl
 * Description：
 * Version：1.0
 */
@Service
@Slf4j
public class AccountTCCServiceImpl implements AccountTCCService {

    @Autowired
    private AccountMapper accountMapper;

    @Autowired
    private AccountFreezeMapper accountFreezeMapper;

    /**
     * @description： 执行try，用户账户扣款，扣减账户可用余额，插入冻结金额记录到冻结表
     * @date：2024/3/26  16:16
     * @return：
     **/
    @Override
    public void deduct(String userId, int money) {

        String xid = RootContext.getXID();
        //通过事务id查询冻结表，如果存在记录，说明cancel已执行，防止业务悬挂，直接结束业务
        AccountFreeze accountFreeze = accountFreezeMapper.selectById(xid);
        if (accountFreeze != null){
            //cancel已执行，不用重复执行
            return;
        }
        //1. 扣减金额
        accountMapper.deduct(userId, money);
        log.debug("扣减金额成功: {},userId={}", xid, userId);
        //2. 添加冻结记录
        AccountFreeze freeze = new AccountFreeze();
        freeze.setFreezeMoney(money);
        freeze.setState(AccountFreeze.State.TRY);
        freeze.setXid(xid);
        freeze.setUserId(userId);
        accountFreezeMapper.insert(freeze);
        log.debug("冻结金额成功! {},userId={}", xid, userId);
        //3. 添加本地事务控制
    }

    /**
     * @description： 确认业务，根据xid删除account_freeze表的冻结记录
     * @date：2024/3/26  16:15
     * @return：
     **/
    @Override
    public boolean confirm(BusinessActionContext ctx) {
        //1. 获取事务Id
        String xid = ctx.getXid();
        log.debug("进入confirm: {}", xid);
        //2. 通过事务Id删除数据
        int count = accountFreezeMapper.deleteById(xid);
        log.debug("删除冻结记录 {},count={}", xid, count);
        return count == 1;
    }

    /**
     * @description： 撤销回滚，需要把冻结金额还原 ，将冻结表金额改为0，状态改为2
     * @date：2024/3/26  16:14
     * @return：
     **/
    @Override
    public boolean cancel(BusinessActionContext ctx) {
        //1. 获取事务Id
        String xid = ctx.getXid();
        //2. 查询冻结记录
        AccountFreeze freeze = accountFreezeMapper.selectById(xid);
        //3. 回滚金额
        String userId = ctx.getActionContext("userId").toString();
        //如果冻结表为空，说明业务已回滚，直接插入一条冻结记录，将状态改为2，金额设置为0
        if (freeze == null ){
            freeze = new AccountFreeze();
            freeze.setFreezeMoney(0);
            freeze.setState(AccountFreeze.State.CANCEL);
            freeze.setXid(xid);
            freeze.setUserId(userId);
            accountFreezeMapper.insert(freeze);
            return true;
        }

        //幂等处理
        if (AccountFreeze.State.CANCEL == freeze.getState()){
            //说明cancel已经处理过一次，无需重复处理
            return true;
        }
        log.debug("开始回滚金额: {},userId={}", xid, userId);
        accountMapper.refund(userId, freeze.getFreezeMoney());
        //4. 更新冻结金额为0，且更新状态为cancel
        freeze = new AccountFreeze();
        freeze.setFreezeMoney(0);
        freeze.setState(AccountFreeze.State.CANCEL);
        freeze.setXid(xid);
        int count = accountFreezeMapper.updateById(freeze);
        log.debug("回滚-更新冻结状态：{}, userId={},count={}", xid, userId, count);
        //5. 本地事务控制
        return count == 1;
    }
}
